/*
 * ActionManager.java
 *
 * Created on 18/07/2005
 *
 * Copyright 2008 the Desert Labs Project (http://desertlabs.us)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package us.desertlabs.ui;

import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.Action;

/**
 * This singleton vends instances of various subtypes of <CODE>Action</CODE>.
 * The instances are managed by keying them to their datatype, ensuring that for
 * any given type of <CODE>Action</CODE>, there is never more than one
 * instance. This allows different parts of an application to interact with a
 * single set of <CODE>Action</CODE> instances, making it much easier to
 * synchronize the state of the UI.
 *
 * @author Brandon Franklin
 */
public class ActionManager {

    /** The singleton instance of this class */
    static private ActionManager INSTANCE = null;

    /**
     * Returns the singleton instance of this class, instantiating it if
     * necessary.
     *
     * @return the singleton instance of this class
     */
    static public ActionManager getInstance() {

        if( INSTANCE == null ) {
            INSTANCE = new ActionManager();
        }

        return INSTANCE;
    }

    /** The <CODE>Map</CODE> of "action groups", keyed by name */
    final private Map<String, List<Class<? extends Action>>> actionGroups;

    /**
     * The <CODE>Map</CODE> of <CODE>Action</CODE> instances, keyed by
     * datatype
     */
    final private Map<Class<? extends Action>, Action> actions;

    /**
     * Creates a new instance of <CODE>ActionManager</CODE> and prepares it
     * for use.
     */
    private ActionManager() {
        actions = new HashMap<Class<? extends Action>, Action>();
        actionGroups = new HashMap<String, List<Class<? extends Action>>>();
    }

    /**
     * Defines an "action group" that consists of the provided <CODE>Action</CODE>s.
     * If the datatype of any of the supplied <CODE>Action</CODE>s is already
     * being managed, then the instance defined here will replace the old one.
     *
     * @param name the reference name of the new group
     * @param actions an array of <CODE>Action</CODE>s to include in the
     *        group, in the desired order
     */
    public void defineActionGroup( final String name, final List<Action> actions ) {
        final List<Class<? extends Action>> types = new LinkedList<Class<? extends Action>>();
        for( final Action action : actions ) {
            putAction( action );
            types.add( action.getClass() );
        }
        actionGroups.put( name, types );
    }

    /**
     * Returns the shared <CODE>Action</CODE> of the requested type.
     *
     * @param type the datatype of the <CODE>Action</CODE> to return
     * @return the shared <CODE>Action</CODE> of the requested type
     */
    public Action getAction( final Class<? extends Action> type ) {
        return actions.get( type );
    }

    /**
     * Returns the <CODE>Action</CODE> instances associated with the "action
     * group" with the supplied name.
     *
     * @param name the reference name of the group
     * @return the <CODE>Action</CODE> instances associated with the "action
     *         group" with the supplied name
     */
    public List<Action> getActionGroup( final String name ) {

        final List<Class<? extends Action>> types = actionGroups.get( name );
        final List<Action> group = new LinkedList<Action>();
        for( final Class<? extends Action> type : types ) {
            group.add( getAction( type ) );
        }
        return group;
    }

    /**
     * Adds the <CODE>Action</CODE> instance that will be shared based on its
     * datatype. If there is already an <CODE>Action</CODE> of the supplied
     * datatype, the new instance will replace the old.
     *
     * @param action the <CODE>Action</CODE> instance to share
     */
    public void putAction( final Action action ) {
        actions.put( action.getClass(), action );
    }

    /**
     * Triggers the supplied <CODE>Action</CODE> from code without requiring
     * any user action. An <CODE>ActionEvent</CODE> will be constructed and
     * sent to the <CODE>Action</CODE>'s <CODE>actionPerformed</CODE>
     * method. The event will have the <CODE>ActionManager</CODE> as its
     * source, and "triggered" as its action command.
     *
     * @param action the <CODE>Action</CODE> to trigger
     */
    public void trigger( final Action action ) {
        final ActionEvent event = new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "triggered" );
        action.actionPerformed( event );
    }
}
